import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;

public class MonteCarloBPCE {
	
	private static final int iteracoesParte1 = 90;
	private static final int iteracoesParte2 = 0;
	private static final int numModeladoresInicial = 10;
	
	private static int numDiagrama;
	private static Map<String, List<String>> mapaDiagramaModelador;	
	private static Map<String, Diagrama> mapaDiagrama;
	private static RandomData rd;
	private static ConexaoBanco bd;	
	private static Map<String, Modelador> mapaModeladores;
	private static Map<String, List<String>> mapaDiagramaAssunto;
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		for(int i = 0; i < 100; i++){
			mapaDiagramaModelador = new HashMap<String, List<String>>();
			numDiagrama = 1;
			mapaDiagrama = new HashMap<String, Diagrama>();
			rd = new RandomDataImpl();
			bd = new ConexaoBanco();		
			mapaModeladores = new HashMap<String, Modelador>();
			mapaDiagramaAssunto = new HashMap<String, List<String>>();
			
			bd.addExecucao(iteracoesParte1 + iteracoesParte2);
	
			inicializaValores();
			rodaMonteCarlo();
		}
		
		System.out.println("Fim");
	}
	
	@SuppressWarnings("unchecked")
	private static void rodaMonteCarlo(){
		int i, qualidadeNovoModelo;
		Modelador modelador;	
		double ptoInicial, ptoVersaoAntiga;
		List<DiagramaAvaliado> listaDiagramaAvaliado;
		List<String> lista, listaDiagrama;
		String nomeAntigo, assunto;		
		
		for(i = 0; i < (iteracoesParte1 + iteracoesParte2); i++){
			List<String> diagramasImportados = new ArrayList<String>();
			listaDiagramaAvaliado = new ArrayList<DiagramaAvaliado>();
			
			modelador = sorteiaModelador(i);
			assunto = "compra";
			
			/*if (i < iteracoesParte1){
				assunto = modelador.getEspecialidade();
			}
			else{
				if (modelador.getEspecialidade().equalsIgnoreCase("compra"))
					assunto = "venda";
				else
					assunto = "compra";
			}*/
			
			listaDiagrama = mapaDiagramaAssunto.get(assunto);
			Collections.sort(listaDiagrama, new ComparaModelos(mapaDiagrama));
			
			ptoVersaoAntiga = 0;
			nomeAntigo = "";
			lista = mapaDiagramaModelador.get(modelador.getNomeModelador());		
			for(String str:lista){
				Diagrama d = mapaDiagrama.get(str);
				
				if (d.getVersao() == modelador.getVersoesPublicadas(assunto)){
					ptoVersaoAntiga = d.getTotalPtos();
					nomeAntigo = d.getNomeDiagrama();
				}
			}			
			
			diagramasImportados = importaDiagramas(modelador, listaDiagrama);			
			pontuaDiagramasImportados(diagramasImportados, modelador, assunto);
			
			//listaDiagramaAvaliado = avaliaDiagramasImportados(diagramasImportados, modelador.getReputacao());
			listaDiagramaAvaliado = avaliaDiagramasImportados(diagramasImportados, modelador.getReputacao() * modelador.getFatorConhecimento(assunto));
			updateKV(listaDiagramaAvaliado, modelador, assunto);
			ptoInicial = pontuaDiagramasAvaliados(listaDiagramaAvaliado);
			qualidadeNovoModelo = sorteiaQualidadeModelo(modelador, assunto);
			
			ptoInicial += ptoVersaoAntiga;
			
			addDiagrama(ptoInicial, modelador, true, diagramasImportados.size(), qualidadeNovoModelo, listaDiagramaAvaliado, nomeAntigo, assunto);
			
			calculaReputacaoModeladores();
		}
	}
	
	private static void updateKV(List<DiagramaAvaliado> listaDiagramaAvaliado, Modelador modelador, String assunto) {
		double ea;
		
		for(DiagramaAvaliado da:listaDiagramaAvaliado){
			Diagrama d = mapaDiagrama.get(da.getNomeDiagrama());
			
			if (d.getDesvioPadrao() > 1){
				ea = 1 - ((Math.abs(da.getNota() - d.getMedia()))/4*d.getDesvioPadrao());
			}
			else{
				ea = 1 - ((Math.abs(da.getNota() - d.getMedia()))/4);
			}
			
			d.addNota(da.getNota());
			
			mapaDiagrama.put(d.getNomeDiagrama(), d);
			modelador.incrementaKV(assunto, ea);
		}
		
		mapaModeladores.put(modelador.getNomeModelador(), modelador);
	}

	private static int sorteiaQualidadeModelo(Modelador modelador, String assunto) {
		List<String> lista;
		int qualidadeModeloAnterior, alteracao, qualidadeModeloNovo;
		
		qualidadeModeloAnterior = 0;
		
		if (modelador.getVersoesPublicadas(assunto) > 0){
			lista = mapaDiagramaModelador.get(modelador.getNomeModelador());		
			for(String str:lista){
				Diagrama d = mapaDiagrama.get(str);
				
				if (d.getVersao() == modelador.getVersoesPublicadas(assunto)){		
					qualidadeModeloAnterior = d.getFatorQualidade();
					break;
				}
			}
			
			alteracao = sorteiaAlteracaoQualidade(modelador);
			
			if (alteracao == 1){
				qualidadeModeloNovo = qualidadeModeloAnterior - 5;
			}
			else if (alteracao == 2){
				qualidadeModeloNovo = qualidadeModeloAnterior;
			}
			else{
				qualidadeModeloNovo = qualidadeModeloAnterior + 5;
			}
		}
		else{
			if (modelador.getQualidade() == 3){
				qualidadeModeloNovo = rd.nextInt(80, 100);
			}
			else if (modelador.getQualidade() == 2){
				qualidadeModeloNovo = rd.nextInt(40, 79);
			}
			else{
				qualidadeModeloNovo = rd.nextInt(0, 39);
			}			
		}
		
		return qualidadeModeloNovo;
	}

	private static int sorteiaAlteracaoQualidade(Modelador modelador) {
		int num = rd.nextInt(1, 100);
		int retorno;
		
		retorno = 0;
		
		if (modelador.getQualidade() == 1){
			if (num <= 50){
				retorno = 1;
			}
			else if (num <= 90){
				retorno = 2;
			}
			else
				retorno = 3;
		}
		else if (modelador.getQualidade() == 2){
			if (num <= 60){
				retorno = 2;
			}
			else if (num <= 80){
				retorno = 1;
			}
			else
				retorno = 3;
		}		
		else{
			if (num <= 50){
				retorno = 3;
			}
			else if (num <= 90){
				retorno = 2;
			}
			else
				retorno = 1;
		}
		
		return retorno;
	}

	private static double pontuaDiagramasAvaliados(List<DiagramaAvaliado> listaDiagramaAvaliado) {
		double ptoInicial, total;
		
		ptoInicial = 0;
		total = 0;
		
		for(DiagramaAvaliado da:listaDiagramaAvaliado){
			Diagrama d = mapaDiagrama.get(da.getNomeDiagrama());
			
			total += (d.getTotalPtos() * (da.getNota()/3));
				
			d.addPontuacao(2, da.getPtoRecebido());
			mapaDiagrama.put(da.getNomeDiagrama(), d);			
		}
		
		if (total > 0){
			ptoInicial = total / (listaDiagramaAvaliado.size());
		}
		
		return ptoInicial;
	}

	private static Modelador sorteiaModelador(int i) {
		int modelador;
		Modelador m;
		
		modelador = i%numModeladoresInicial;
		
		m = mapaModeladores.get(Integer.toString(modelador+1));
		
		return m;
	}

	private static List<DiagramaAvaliado> avaliaDiagramasImportados(List<String> diagramasImportados, double reputacao) {
        double p, b;
        int nota;
        List<DiagramaAvaliado> listaDiagramaAvaliado;
        
        listaDiagramaAvaliado = new ArrayList<DiagramaAvaliado>();
        
        for(String str: diagramasImportados){        	
        	Diagrama d = mapaDiagrama.get(str);        	
        	
        	b = Math.random();
        	
        	if (reputacao > 0.15){
        		p = 0.9;
        	}
        	else if(reputacao > 0.10){
        		p = 0.6;
        	}
        	else{
        		p = 0.3;
        	}
        	
        	if (b <= p){
        		b = Math.random();
        		
	        	if (d.getQualidade() == 3){
	        		if (d.getFatorQualidade() >= 100){
	        			p = 0.05;
	        		}
	        		else if (d.getFatorQualidade() > 80){
	        			p = 1 - ((d.getFatorQualidade() - 80.0) / 20.0);
	        		}
	        		else
	        			p = 0.95;
	        		
	        		if (b <= p){
	        			nota = 2;
	        		}
	        		else{
	        			nota = 3;
	        		}
	        	}
	        	else if (d.getQualidade() == 2){
	        		if (d.getFatorQualidade() == 79){
	        			p = 0.025;
	        		}
	        		else if (d.getFatorQualidade() > 40){
	        			p = 1 - ((d.getFatorQualidade() - 40.0) / 40.0);
	        		}
	        		else
	        			p = 0.975;
	        		
	        		if (b <= p){
	        			nota = 1;
	        		}
	        		else{
	        			nota = 2;
	        		}        		
	        	}
	        	else{
	        		if (d.getFatorQualidade() == 39){
	        			p = 0.975;
	        		}
	        		else if (d.getFatorQualidade() > 1){
	        			p = 1 - ((39.0 - d.getFatorQualidade()) / 40.0);
	        		}
	        		else
	        			p = 0.025;
	        		
	        		if (b <= p){
	        			nota = 1;
	        		}
	        		else{
	        			nota = 0;
	        		}
	        	}
        	}
        	else{
        		nota = rd.nextInt(0, 3);        		        		
        	}
        	
        	DiagramaAvaliado da = new DiagramaAvaliado();
        	da.setNomeDiagrama(d.getNomeDiagrama());
        	da.setNomeModelador(d.getNomeModelador());
        	da.setNota(nota);
        	da.setPtoRecebido(reputacao * nota);
        	listaDiagramaAvaliado.add(da);
        }
        
		return listaDiagramaAvaliado;				
	}

	private static void calculaReputacaoModeladores() {
		float ptoModelador, total;
		
		total = 0;
		
		for(String nome: mapaModeladores.keySet()){
			total += calculaPontuacaoModelador(nome);
		}
		
		for(String nome: mapaModeladores.keySet()){
			ptoModelador = calculaPontuacaoModelador(nome);
			Modelador m = mapaModeladores.get(nome);
			
			if (total == 0)
				m.setReputacao(0);
			else
				m.setReputacao(ptoModelador / total);				
		}
	}

	private static float calculaPontuacaoModelador(String nomeModelador) {
		float total, retorno;
		List<String> lista;
		int avaliou;
		
		lista = mapaDiagramaModelador.get(nomeModelador);
		total = 0;
		avaliou = 0;
		retorno = 0;
		
		if (lista != null){
			for(String nome: lista){
				Diagrama d = mapaDiagrama.get(nome);
				total += d.getTotalPtos();
				
				if (d.getAvaliou())
					avaliou++;
			}
			
			if (total > 0){
				retorno = total / lista.size();
				retorno += avaliou;
			}
		}
		
		return retorno;
	}

	private static void pontuaDiagramasImportados(List<String> diagramasImportados, Modelador modelador, String assunto) {
		for(String str: diagramasImportados){			
			Diagrama d = mapaDiagrama.get(str);
			
			if (! d.getNomeModelador().equalsIgnoreCase(modelador.getNomeModelador())){
				d.addPontuacao(1, modelador.getReputacao() * modelador.getFatorConhecimento(assunto));
				//d.addPontuacao(1, modelador.getReputacao());
				mapaDiagrama.put(str, d);
			
				bd.addImportacaoDiagrama(d, modelador.getReputacao() * modelador.getFatorConhecimento(assunto));
				//bd.addImportacaoDiagrama(d, modelador.getReputacao());
			}
		}
		
		calculaReputacaoModeladores();
	}

	private static List<String> importaDiagramas(Modelador modelador, List<String> listaDiagrama) {
		List<Double> probabilidadeImportar;
		double diagrama, minp, p;
		boolean achou;
		List<String> listaDiagramaImportado;
		int i;
		
		listaDiagramaImportado = new ArrayList<String>();				
		i = 1;		
		
		probabilidadeImportar = retornaProbabilidades(listaDiagrama.subList(0, Math.min(5, listaDiagrama.size())));
		minp = probabilidadeImportar.get(probabilidadeImportar.size() - 1);
		
		while(true){
			achou = false;
			
			for(int j = 0; j < probabilidadeImportar.size(); j++){
				p = Math.random();
				//diagrama = probabilidadeImportar.get(j) - (minp * Math.log(i));
				if (i > 1)
					diagrama = probabilidadeImportar.get(j) / Math.log(i + 1);
				else
					diagrama = probabilidadeImportar.get(j) - (minp * Math.log(i));
				
				if (p <= diagrama){
					String d = listaDiagrama.get(j);
					
					if ((! listaDiagramaImportado.contains(d)) && (! mapaDiagrama.get(d).getNomeModelador().equalsIgnoreCase(modelador.getNomeModelador()))){
						achou = true;
						listaDiagramaImportado.add(d);
						break;						
					}					
				}
				else if (diagrama  < 0){
					break;
				}
			}
				
			i++;
				
			if (! achou)
				break;
		}
		
		return listaDiagramaImportado;
	}

	private static List<Double> retornaProbabilidades(List<String> diagramas) {
		List<Double> retorno;
		int i;
		double p, totPto;
		Diagrama d;
		
		retorno = new ArrayList<Double>();
		totPto = calculaPontuacaoTotal(diagramas);
		
		if(diagramas.size() > 1){
			for(i = 0; i < diagramas.size(); i++){
				d = mapaDiagrama.get(diagramas.get(i));
				p = (d.getTotalPtos() / totPto);
				retorno.add(p);
			}
		}
		else
			retorno.add(0.5);
		
		Collections.sort(retorno);
		Collections.reverse(retorno);
		
		return retorno;
	}

	private static float calculaPontuacaoTotal(List<String> lista) {
		int i;
		Diagrama d;
		float retorno = 0;
		
		for (i = 0; i < lista.size(); i++){
			d = mapaDiagrama.get(lista.get(i));			
			retorno += d.getTotalPtos();
		}
		
		return retorno;
	}

	private static void inicializaValores(){
		int i;
		Modelador modelador;
		int qualidadeModelo;
		String assunto;
		
		qualidadeModelo = 0;
		
		for(i = 0; i < numModeladoresInicial; i++){
			modelador = addModelador(i+1);
			
			if (modelador.getQualidade() == 3){
				qualidadeModelo = rd.nextInt(80, 100);
			}
			else if (modelador.getQualidade() == 2){
				qualidadeModelo = rd.nextInt(40, 79);
			}
			else if (modelador.getQualidade() == 1){
				qualidadeModelo = rd.nextInt(0, 39);
			}
			
			/*int par = (i+1)%2; 
			
			if (par == 0)
				assunto = "compra";
			else
				assunto = "venda";*/
			
			assunto = "compra";
			
			addDiagrama(0, modelador, false, 0, qualidadeModelo, null, "", assunto);
		}
	}
	
	private static void addDiagrama(double ptoInicial, Modelador modelador, boolean avaliou, int qtdeDiagramasImportados, int qualidadeModelo,
			List<DiagramaAvaliado> listaDiagramaAvaliado, String nomeAntigo, String assunto) {
		List<String> lista;
		String nomeDiagrama;
		
		nomeDiagrama = Integer.toString(numDiagrama);
		
		//double reputacao = modelador.getReputacao();
		double reputacao = modelador.getReputacao() * modelador.getFatorConhecimento(assunto);
		
		if (reputacao == 1)
			ptoInicial += 1;
		else
			ptoInicial += Math.pow(Math.E, 8 * reputacao);
		
		Diagrama diagrama = new Diagrama();
		diagrama.setNomeDiagrama(nomeDiagrama);
		diagrama.setPtoInicial(ptoInicial);
		diagrama.setNomeModelador(modelador.getNomeModelador());
		diagrama.setAvaliou(avaliou);
		diagrama.setQtdeDiagramasImportados(qtdeDiagramasImportados);
		diagrama.setListaDiagramaAvaliado(listaDiagramaAvaliado);
		diagrama.setFatorQualidade(qualidadeModelo);
		diagrama.setVersao(modelador.novaVersao(assunto));
		diagrama.setAssunto(assunto);
		
		mapaDiagrama.put(nomeDiagrama, diagrama);
		
		List<String> listaDiagrama = mapaDiagramaAssunto.get(assunto);
		
		if (listaDiagrama == null){
			listaDiagrama = new ArrayList<String>();
		}
		
		if (nomeAntigo.equalsIgnoreCase("")){
			listaDiagrama.add(nomeDiagrama);
		}
		else{
			listaDiagrama.remove(nomeAntigo);
			listaDiagrama.add(nomeDiagrama);
		}
		
		mapaDiagramaAssunto.put(assunto, listaDiagrama);
		
		if (mapaDiagramaModelador.containsKey(modelador.getNomeModelador())){
			lista = mapaDiagramaModelador.get(modelador.getNomeModelador());
			lista.add(nomeDiagrama);
		}
		else{
			lista = new ArrayList<String>();
			lista.add(nomeDiagrama);			
		}
		mapaDiagramaModelador.put(modelador.getNomeModelador(), lista);
		
		bd.addDiagrama(diagrama);
		numDiagrama++;
	}

	private static Modelador addModelador(int i) {
		String tmp = Integer.toString(i);
		double reputacao;
		int valor, par;
		String assunto;
		
		par = i%2;
		
		if (i < 3){
			reputacao = 0.15;
			valor = 3;
		}
		else if (i < 8){
			reputacao = 0.10;
			valor = 2;
		}
		else{
			reputacao = 0.05;
			valor = 1;
		}
		
		if (par == 0)
			assunto = "compra";
		else
			assunto = "venda";
		
		Modelador modelador = new Modelador();
		modelador.setNomeModelador(tmp);		
		modelador.setReputacao(reputacao);
		modelador.setQualidade(valor);
		modelador.setEspecialidade(assunto);
		modelador.incrementaKV(assunto, valor);
		
		mapaModeladores.put(tmp, modelador);
		
		bd.addModelador(modelador);
		
		return modelador;
	}
}